// Model Predictive Control, 16. Sep 2014
// Kompilieren: g++ aslesa.cpp -lBox2D -lsfml-graphics -lsfml-window -lsfml-system
// Aufruf: ./a.out 

#include <SFML/Graphics.hpp>
#include <Box2D/Box2D.h>
#include <iostream>
#include <math.h>
#include <unistd.h> // für sleep Befehl
#include <stdlib.h>  // srand, rand 

const int RESOLUTIONX=600;
const int RESOLUTIONY=400;
const int BOXWIDTH=60;
const int BOXHEIGTH=15;
const int PLANMAX=1000; // Suchhorizont 1000
const int SCHRITTWEITE=PLANMAX/5; // PLANMAX/5
const int WELTMAX=100;
int MouseX=100, MouseY=200;

// Obergrenzen für Datenstruktur
const int JOINTCONSTMAX=20;
const int BOXCONSTMAX=50;
int JointMax=0;
int BoxMax=0;

sf::RenderWindow window(sf::VideoMode(RESOLUTIONX,RESOLUTIONY), "window");

struct jointdef {
  int jointid;
  int BodyA; // id of BodyA
  int BodyB; // id of BodyB
  b2Vec2 AnchorA; // für localAnchorA.Set
  b2Vec2 AnchorB;
  int rotate[PLANMAX]; // Gelenkeinstellung im Zeitverlauf
};

class Layer1 {
public:
  struct boxdef {
    int boxid;
    b2Vec2 position;
    int width, height;
    float angle;
    b2Vec2 velocity;
    float angularVel;
    int type; // 0=static, 1=dynamic
  };
  struct bodyUserData { // für Box2D
    int id;
  };
  struct jointUserData { // für Box2D
    int id;
  };

  boxdef box[BOXCONSTMAX];
  jointdef joint[JOINTCONSTMAX];
  b2World world;
// ----------------- Layer1 ------------------------
  int Fehlerwert()
  {
    double temp=0;
    for (int i=0; i<JointMax; i++) {
      temp=temp+sqrt(pow(MouseX-box[i].position.x,2)+pow(MouseY-box[i].position.y,2));
    }

    // temp=sqrt(pow(MouseX-box[3].position.x,2)+pow(MouseY-box[3].position.y,2));
    return temp;
  }

  void ZufallsplanErstellen()
  {
    int Zufallszahl;

    for (int i=0; i<JointMax; i++) {
      for (int zeit=0; zeit<PLANMAX; zeit++) {
        if((zeit%SCHRITTWEITE)==0) { // nur jeden 10. Schritt eine Aktion
          Zufallszahl=rand()%3+1; // generate number between 1 and 3
          Zufallszahl=Zufallszahl-2; // Wertebereich auf (-1, 0, 1) setzen
        } // if
        joint[i].rotate[zeit]=Zufallszahl;
      } // for zeit
    } // for i

  }

  void InitDatenstruktur()
  {
    // Rahmen unten 0
    box[BoxMax].boxid=BoxMax;
    box[BoxMax].position=b2Vec2(0+RESOLUTIONX/2,RESOLUTIONY-10); 
    box[BoxMax].width=RESOLUTIONX; 
    box[BoxMax].height=5; 
    box[BoxMax].type=0;
    BoxMax++;

    // Rahmen rechts,1
    box[BoxMax].boxid=BoxMax;
    box[BoxMax].position=b2Vec2(RESOLUTIONX-10,0+RESOLUTIONY/2);  
    box[BoxMax].width=5; 
    box[BoxMax].height=RESOLUTIONY; 
    box[BoxMax].type=0;
    BoxMax++;

    // Rahmen links,2
    box[BoxMax].boxid=BoxMax;
    box[BoxMax].position=b2Vec2(10,RESOLUTIONY/2); 
    box[BoxMax].width=5; 
    box[BoxMax].height=RESOLUTIONY; 
    box[BoxMax].type=0;
    BoxMax++;

    // Box dynamisch,3
    box[BoxMax].boxid=BoxMax;
    box[BoxMax].position=b2Vec2(50,100); 
    box[BoxMax].width=BOXWIDTH;
    box[BoxMax].height=BOXHEIGTH; 
    box[BoxMax].type=1;
    box[BoxMax].angle=180;
    BoxMax++;

    // Box dynamisch,4
    box[BoxMax].boxid=BoxMax;
    box[BoxMax].position=b2Vec2(100,100); 
    box[BoxMax].width=BOXWIDTH;
    box[BoxMax].height=BOXHEIGTH; 
    box[BoxMax].type=1;
    box[BoxMax].angle=180;
    BoxMax++;

    // Box dynamisch,5
    box[BoxMax].boxid=BoxMax;
    box[BoxMax].position=b2Vec2(150,100); 
    box[BoxMax].width=BOXWIDTH;
    box[BoxMax].height=BOXHEIGTH; 
    box[BoxMax].type=1;
    box[BoxMax].angle=180;
    BoxMax++;

    // Box dynamisch,6
    box[BoxMax].boxid=BoxMax;
    box[BoxMax].position=b2Vec2(200,100); 
    box[BoxMax].width=BOXWIDTH;
    box[BoxMax].height=BOXHEIGTH; 
    box[BoxMax].type=1;
    box[BoxMax].angle=180;
    BoxMax++;

    // Box dynamisch,7
    box[BoxMax].boxid=BoxMax;
    box[BoxMax].position=b2Vec2(250,100); 
    box[BoxMax].width=BOXWIDTH;
    box[BoxMax].height=BOXHEIGTH; 
    box[BoxMax].type=1;
    box[BoxMax].angle=180;
    BoxMax++;

    // Box dynamisch,8
    box[BoxMax].boxid=BoxMax;
    box[BoxMax].position=b2Vec2(300,100); 
    box[BoxMax].width=BOXWIDTH;
    box[BoxMax].height=BOXHEIGTH; 
    box[BoxMax].type=1;
    box[BoxMax].angle=180;
    BoxMax++;

    // Box dynamisch,9
    box[BoxMax].boxid=BoxMax;
    box[BoxMax].position=b2Vec2(350,100); 
    box[BoxMax].width=BOXWIDTH;
    box[BoxMax].height=BOXHEIGTH; 
    box[BoxMax].type=1;
    box[BoxMax].angle=180;
    BoxMax++;

    // Box dynamisch,10
    box[BoxMax].boxid=BoxMax;
    box[BoxMax].position=b2Vec2(400,100); 
    box[BoxMax].width=BOXWIDTH;
    box[BoxMax].height=BOXHEIGTH; 
    box[BoxMax].type=1;
    box[BoxMax].angle=180;
    BoxMax++;

    // Box dynamisch,11
    box[BoxMax].boxid=BoxMax;
    box[BoxMax].position=b2Vec2(450,100); 
    box[BoxMax].width=BOXWIDTH;
    box[BoxMax].height=BOXHEIGTH; 
    box[BoxMax].type=1;
    box[BoxMax].angle=180;
    BoxMax++;

    // Hinderniss statisch,10
    box[BoxMax].boxid=BoxMax;
    box[BoxMax].position=b2Vec2(450,360); 
    box[BoxMax].width=BOXWIDTH/4;
    box[BoxMax].height=BOXHEIGTH*4; 
    box[BoxMax].type=0;
    box[BoxMax].angle=0;
    BoxMax++;

    // Hinderniss statisch,11
    box[BoxMax].boxid=BoxMax;
    box[BoxMax].position=b2Vec2(450,230); 
    box[BoxMax].width=BOXWIDTH/4;
    box[BoxMax].height=BOXHEIGTH*8; 
    box[BoxMax].type=0;
    box[BoxMax].angle=0;
    BoxMax++;

    // Hinderniss statisch,10
    box[BoxMax].boxid=BoxMax;
    box[BoxMax].position=b2Vec2(350,360); 
    box[BoxMax].width=BOXWIDTH/4;
    box[BoxMax].height=BOXHEIGTH*8; 
    box[BoxMax].type=0;
    box[BoxMax].angle=0;
    BoxMax++;

    // Hinderniss statisch,11
    box[BoxMax].boxid=BoxMax;
    box[BoxMax].position=b2Vec2(350,200); 
    box[BoxMax].width=BOXWIDTH/4;
    box[BoxMax].height=BOXHEIGTH*6; 
    box[BoxMax].type=0;
    box[BoxMax].angle=0;
    BoxMax++;


    // Hinderniss statisch,12
    box[BoxMax].boxid=BoxMax;
    box[BoxMax].position=b2Vec2(140,300); 
    box[BoxMax].width=BOXWIDTH*3;
    box[BoxMax].height=BOXHEIGTH; 
    box[BoxMax].type=0;
    box[BoxMax].angle=0;
    BoxMax++;


    // Joint,0
    joint[JointMax].jointid=JointMax;
    joint[JointMax].BodyA=3;
    joint[JointMax].BodyB=4;
    joint[JointMax].AnchorA=b2Vec2(BOXWIDTH/2,0);
    joint[JointMax].AnchorB=b2Vec2(BOXWIDTH/2,0);
    JointMax++;

    // Joint,1
    joint[JointMax].jointid=JointMax;
    joint[JointMax].BodyA=4;
    joint[JointMax].BodyB=5;
    joint[JointMax].AnchorA=b2Vec2(-BOXWIDTH/2,0);
    joint[JointMax].AnchorB=b2Vec2(BOXWIDTH/2,0);
    JointMax++;

    // Joint,2
    joint[JointMax].jointid=JointMax;
    joint[JointMax].BodyA=5;
    joint[JointMax].BodyB=6;
    joint[JointMax].AnchorA=b2Vec2(-BOXWIDTH/2,0);
    joint[JointMax].AnchorB=b2Vec2(BOXWIDTH/2,0);
    JointMax++;

    // Joint,3
    joint[JointMax].jointid=JointMax;
    joint[JointMax].BodyA=6;
    joint[JointMax].BodyB=7;
    joint[JointMax].AnchorA=b2Vec2(-BOXWIDTH/2,0);
    joint[JointMax].AnchorB=b2Vec2(BOXWIDTH/2,0);
    JointMax++;

    // Joint,4
    joint[JointMax].jointid=JointMax;
    joint[JointMax].BodyA=7;
    joint[JointMax].BodyB=8;
    joint[JointMax].AnchorA=b2Vec2(-BOXWIDTH/2,0);
    joint[JointMax].AnchorB=b2Vec2(BOXWIDTH/2,0);
    JointMax++;

    // Joint,5
    joint[JointMax].jointid=JointMax;
    joint[JointMax].BodyA=8;
    joint[JointMax].BodyB=9;
    joint[JointMax].AnchorA=b2Vec2(-BOXWIDTH/2,0);
    joint[JointMax].AnchorB=b2Vec2(BOXWIDTH/2,0);
    JointMax++;

    // Joint,6
    joint[JointMax].jointid=JointMax;
    joint[JointMax].BodyA=9;
    joint[JointMax].BodyB=10;
    joint[JointMax].AnchorA=b2Vec2(-BOXWIDTH/2,0);
    joint[JointMax].AnchorB=b2Vec2(BOXWIDTH/2,0);
    JointMax++;

    // Joint,7
    joint[JointMax].jointid=JointMax;
    joint[JointMax].BodyA=10;
    joint[JointMax].BodyB=11;
    joint[JointMax].AnchorA=b2Vec2(-BOXWIDTH/2,0);
    joint[JointMax].AnchorB=b2Vec2(BOXWIDTH/2,0);
    JointMax++;


  }

  void DatenImportieren(boxdef mybox[], jointdef myjoint[])
  {
    std::copy(mybox,mybox+BoxMax,box); // box[]=mybox[]
    std::copy(myjoint,myjoint+JointMax,joint); // joint[]=myjoint[]
  }

  void AusgabeText()
  {
    std::cout << "Ausgabe box[]\n";
    for (int i=0; i<BoxMax; i++) {
      std::cout << i << ": ";      
      std::cout << box[i].boxid << " ";      
      std::cout << box[i].position.x << " ";
      std::cout << box[i].position.y << " ";
      std::cout << box[i].width << " ";
      std::cout << box[i].height << " ";
      std::cout << box[i].angle << " ";
      std::cout << box[i].velocity.x << " ";
      std::cout << box[i].velocity.y << " ";
      std::cout << box[i].angularVel << " ";
      std::cout << box[i].type << " ";
      std::cout << "\n";
    }

    std::cout << "Ausgabe joint[]\n";
    for (int i=0; i<JointMax; i++) {
      std::cout << i << ": ";      
      std::cout << joint[i].jointid << " ";      
      std::cout << joint[i].BodyA << " ";
      std::cout << joint[i].BodyB << " ";
      std::cout << joint[i].AnchorA.x << " ";
      std::cout << joint[i].AnchorA.y << " ";
      std::cout << joint[i].AnchorB.x << " ";
      std::cout << joint[i].AnchorB.y << " ";
      std::cout << "\n";
      for (int k=0; k<PLANMAX; k++) 
        std::cout << joint[i].rotate[k] << " ";
      std::cout << "\n";
    } // for i
  }

  void AusgabeGrafisch(int fill)
  // fill=0 -> transparent
  // fill=1 -> mit Farbe
  // fill=2 -> transparent, Rahmen in rot
  {

    if (fill==1)
      window.clear();

    for (int i=0; i<BoxMax; i++) {
      sf::RectangleShape rectangle(sf::Vector2f(box[i].width, box[i].height)); 
      rectangle.setPosition(box[i].position.x,box[i].position.y); 
      // Mittelpunkt setzen für Rotation
      // auf Schwerpunkt, d.h. BreiteRechteck/2, LängeRechteck/2
      rectangle.setOrigin(box[i].width/2.f, box[i].height/2.f); 
      rectangle.setRotation(box[i].angle * 180/b2_pi);

      if (fill==0) {
        rectangle.setFillColor(sf::Color::Transparent);
        rectangle.setOutlineThickness(1); // Rahmen
        if (box[i].type==0) rectangle.setOutlineThickness(0); // Rahmen
        rectangle.setOutlineColor(sf::Color(238,201,0));
      }
      if (fill==1) {
        rectangle.setFillColor(sf::Color(205,16,118));
        if (box[i].type==1) {
          rectangle.setOutlineThickness(2); // Rahmen
        } // if type==1
        rectangle.setOutlineColor(sf::Color(238,201,0));
      } // if fill==1
      if (fill==2) {
        rectangle.setFillColor(sf::Color::Transparent);
        rectangle.setOutlineThickness(4); // Rahmen
        if (box[i].type==0) rectangle.setOutlineThickness(0); // Rahmen
        rectangle.setOutlineColor(sf::Color(255,0,0));
      }

      window.draw(rectangle);
    }

    // Kreis zeichnen bei Zielkoordinaten
    int MARKERRADIUS=8;
    sf::CircleShape circleZiel(MARKERRADIUS);
    circleZiel.setPosition(MouseX-MARKERRADIUS,MouseY-MARKERRADIUS); // Kreis zentrieren
    circleZiel.setFillColor(sf::Color(255,0,0));
    window.draw(circleZiel);

    window.display();
  }

  // Konstruktor
  Layer1() :world(b2Vec2(0.f, 9.8f))
  {
    // Box Array auf 0 setzen
    for (int i=0; i<BOXCONSTMAX; i++) {
      box[i].boxid=0;
      box[i].position=b2Vec2(0,0);
      box[i].width=0;
      box[i].height=0;
      box[i].angle=0;
      box[i].velocity=b2Vec2(0,0);
      box[i].angularVel=0; 
      box[i].type=0;
    }
    // Joint Array auf 0 setzen
    for (int i=0; i<JOINTCONSTMAX; i++) {
      joint[i].jointid=0;
      joint[i].BodyA=0;
      joint[i].BodyB=0;
      joint[i].AnchorA=b2Vec2(0,0);
      joint[i].AnchorB=b2Vec2(0,0);
      for (int k=0; k<PLANMAX; k++)
        joint[i].rotate[k]=1;
    }

  } // Konstructor

// ----------------- Layer2 ------------------------
  void InitBox2D()
  {
    for (int i=0; i<BoxMax; i++)
      CreateBox(box[i]);
    for (int i=0; i<JointMax; i++)
      CreateJoint(joint[i]);
  } // InitBox2D()

  void GelenkeEinstellen(int zeit)
  {
    // Iterating over joints
    for (b2Joint* j = world.GetJointList(); j != 0; j = j->GetNext()) 
    {
      jointUserData* udStruct = (jointUserData*)j->GetUserData();
      b2RevoluteJoint* jointtyp = (b2RevoluteJoint*)j; // Typcast um auf vererbte Klasse zugreifen zu können
      jointtyp->SetMotorSpeed(joint[udStruct->id].rotate[zeit]);
    } // for b2joint*
  }

  void CreateJoint(jointdef myjoint)
  {
    // BodyA und BodyB suchen
    b2Body* BodyA;
    b2Body* BodyB;
    for (b2Body* i = world.GetBodyList(); i != 0; i = i->GetNext()) {
      bodyUserData* udStruct = (bodyUserData*)i->GetUserData();
      if (udStruct->id == myjoint.BodyA)
        BodyA=i;
      if (udStruct->id == myjoint.BodyB)
        BodyB=i;
    } // for 

    // Joint erstellen
    b2RevoluteJointDef revoluteJointDef;
    revoluteJointDef.bodyA = BodyA;
    revoluteJointDef.bodyB = BodyB;
    revoluteJointDef.collideConnected = false;
    revoluteJointDef.localAnchorA.Set(myjoint.AnchorA.x,myjoint.AnchorA.y);
    revoluteJointDef.localAnchorB.Set(myjoint.AnchorB.x,myjoint.AnchorB.y);
    revoluteJointDef.enableMotor = true;
    revoluteJointDef.motorSpeed = 0.0f;
    revoluteJointDef.maxMotorTorque = 1000000;

    // Limits
    revoluteJointDef.enableLimit = true;
    revoluteJointDef.lowerAngle = -160 * b2_pi/180;
    revoluteJointDef.upperAngle =  160 * b2_pi/180;

    //world.CreateJoint(&revoluteJointDef);
    b2Joint* Jointtemp = world.CreateJoint(&revoluteJointDef);

    // SetUserData, http://www.iforce2d.net/b2dtut/user-data
    jointUserData* myStruct = new jointUserData;
    myStruct->id = myjoint.jointid; 
    Jointtemp->SetUserData(myStruct);
  }

  void CreateBox(boxdef mybox)
  {
    b2BodyDef BodyDef;
    BodyDef.position = mybox.position;
    if (mybox.type==0) BodyDef.type = b2_staticBody;
    if (mybox.type==1) BodyDef.type = b2_dynamicBody;
    BodyDef.angle = mybox.angle;
    b2Body* Body = world.CreateBody(&BodyDef);

    // SetUserData, http://www.iforce2d.net/b2dtut/user-data
    bodyUserData* myStruct = new bodyUserData;
    myStruct->id = mybox.boxid; 
    Body->SetUserData(myStruct);

    // Set Velocity 
    Body->SetLinearVelocity(mybox.velocity);
    Body->SetAngularVelocity(mybox.angularVel);

    // Set Fixture
    b2PolygonShape Shape;
    Shape.SetAsBox(mybox.width/2.f, mybox.height/2.f); // halbe Breite, halbe Höhe
    b2FixtureDef FixtureDef;
    FixtureDef.density = 1.f;
    FixtureDef.friction = 0.7f;
    FixtureDef.shape = &Shape;
    Body->CreateFixture(&FixtureDef);
  }

  void Update()
  { // kopiert Box2D Welt auf Datenstruktur box[]
    for (b2Body* i = world.GetBodyList(); i != 0; i = i->GetNext()) {
      bodyUserData* udStruct = (bodyUserData*)i->GetUserData();
      box[udStruct->id].position = i->GetPosition();
      box[udStruct->id].angle = i->GetAngle();
      box[udStruct->id].velocity = i->GetLinearVelocity();
      box[udStruct->id].angularVel = i->GetAngularVelocity();
      // box[udStruct->id].width 
      // box[udStruct->id].height
      // box[udStruct->id].type
      // ... bleiben unverändert 

      // offen: rotational inertia
/*
      std::cout << "id " << udStruct->id << ": ";
      std::cout << i->GetInertia();
      std::cout << "\n";
*/
    }
      
  }

  void Einzelschritt()
  {
    const float timeStep = 1.0f / 30.0f;
    const int velocityIterations = 1; // kleiner=Objekte hüpfen
    const int positionIterations = 3;

    world.Step(timeStep, velocityIterations, positionIterations);
  }

}; // class Layer1


class Engine {
public:
  Layer1 hauptwelt; // muss hier deklariert sein, damit von Überall Zugriff
  int stepHaupt;
  jointdef besterPlan[JOINTCONSTMAX];

  void PlanMarkieren()
  {
    Layer1 simulation; // Reset des Objektes
    simulation.DatenImportieren(hauptwelt.box, besterPlan);
    simulation.InitBox2D();
    for (int a=0; a<PLANMAX; a++) {
      simulation.GelenkeEinstellen(a%PLANMAX);
      simulation.Einzelschritt();
      simulation.Update();
    } // for a
    simulation.AusgabeGrafisch(2);
  }

  void EreignisseAbfragen()
  {
    sf::Event event;
    while (window.pollEvent(event))
    {
      if (event.type == sf::Event::Closed)
        window.close();
      if (event.type == sf::Event::MouseMoved) {
        MouseX = sf::Mouse::getPosition(window).x;
        MouseY = sf::Mouse::getPosition(window).y;
      }
      if (event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Left) {
        std::cout << "linke Mausetaste gedrückt, erstelle neue Box\n";
        std::cout << MouseX << " " << MouseY << "\n";
        // Box dynamisch
        hauptwelt.box[BoxMax].boxid=BoxMax;
        hauptwelt.box[BoxMax].position=b2Vec2(MouseX,MouseY); 
        hauptwelt.box[BoxMax].width=BOXWIDTH;
        hauptwelt.box[BoxMax].height=BOXHEIGTH; 
        hauptwelt.box[BoxMax].type=1;
        hauptwelt.box[BoxMax].angle=0;
        hauptwelt.CreateBox(hauptwelt.box[BoxMax]);
        BoxMax++;
      }

    } // while window poll event
  }

  void Prediction1()
  { // führt simple Prognose aus
    Layer1 simulation; // Reset des Objektes
    simulation.DatenImportieren(hauptwelt.box, hauptwelt.joint);
    simulation.InitBox2D();

    for (int a=0; a<PLANMAX; a++) {
      usleep(10000);
      if (a%10==0) simulation.AusgabeGrafisch(0);
      simulation.GelenkeEinstellen(a%PLANMAX);
      simulation.Einzelschritt();
      simulation.Update();
    } // for a

  }

  void Prediction2()
  { // berechnet mehrere Alternativen
    for(int i=0; i<WELTMAX;i++) {
      Layer1 simulation; // Reset des Objektes
      simulation.DatenImportieren(hauptwelt.box, hauptwelt.joint);
      simulation.ZufallsplanErstellen();
      simulation.InitBox2D();

      for (int a=0; a<PLANMAX; a++) {
        simulation.GelenkeEinstellen(a%PLANMAX);
        simulation.Einzelschritt();
        simulation.Update();
      } // for a
      simulation.AusgabeGrafisch(0);
      usleep(100000);
    } // for i
    usleep(1000000);

  }

  void Prediction3()
  { // berechnet mehrere Alternativen und färbt die beste rot ein
    int minFehler=100000;
    for(int i=0; i<WELTMAX;i++) {
//      if (i%100==0) std::cout << i << "\n";
      Layer1 simulation; // Reset des Objektes
      simulation.DatenImportieren(hauptwelt.box, hauptwelt.joint);
      simulation.ZufallsplanErstellen();
      simulation.InitBox2D();
      for (int a=0; a<PLANMAX; a++) {
        simulation.GelenkeEinstellen(a%PLANMAX);
        simulation.Einzelschritt();
        simulation.Update();
      } // for a
      simulation.AusgabeGrafisch(0);
      int temp=simulation.Fehlerwert();
      if (temp<minFehler) {
        minFehler=temp;
          // besterPlan = simulation.joint;
        std::copy(simulation.joint,simulation.joint+JointMax,besterPlan); 
      }
    } // for i

    PlanMarkieren();
    hauptwelt.DatenImportieren(hauptwelt.box, besterPlan); // Plan auf hauptwelt kopieren
    usleep(100000);

  }


  void run()
  { 
    srand (time(NULL)); // initialize random seed
    long a=pow(3,PLANMAX/SCHRITTWEITE); // 3 hoch PLANMAX/SCHRITTWEITE
    int b=WELTMAX;
    std::cout << "Anzahl Möglichkeiten " << a << "\n";
    std::cout << "davon durchsucht " << b << "\n";

    hauptwelt.InitDatenstruktur();
    hauptwelt.InitBox2D();

    for(stepHaupt=0; stepHaupt<1000000;stepHaupt++) {
      usleep(1500);
      EreignisseAbfragen();
      hauptwelt.AusgabeGrafisch(1);
      hauptwelt.GelenkeEinstellen(stepHaupt%PLANMAX);
      hauptwelt.Einzelschritt();
      hauptwelt.Update();
      if (stepHaupt%PLANMAX==0) {
        //hauptwelt.AusgabeText();
        Prediction3();
      } // if
    } // for 

  } // run()
}; // class Engine


int main()
{
//  usleep(10000000);

  Engine myEngine;
  myEngine.run();
  usleep(1000000000);
}
